﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Filtry.Logic;
using Filtry.Data;


namespace Filtry
{
    public partial class Main : Form
    {
        public Dictionary<int, Zadanie> Zadania { get; set; }
        public CancellationTokenSource TokenG { get; set; }
        public CancellationTokenSource TokenB { get; set; }
        public List<SNR> SNRS { get; set; }

        public Main()
        {
            InitializeComponent();
            TokenB = new CancellationTokenSource();
            TokenG = new CancellationTokenSource();
            Zadania = new Dictionary<int, Zadanie>();
            SNRS = new List<SNR>();

        }

        private async void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.InitialDirectory = "c:\\";
            openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 2;
            openFileDialog.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                var node = new TreeNode(String.Format("Otwórz Plik {0}", openFileDialog.SafeFileName));
                treeView1.Nodes.Add(node);
                var parameters = new Parameters { FilePath = openFileDialog.FileName };
                var zadanie = new Zadanie(OpenImage, parameters);
                Zadania.Add(node.GetHashCode(), zadanie);
            }
        }



        private void AddNodeToTree(TreeNode node)
        {
            if (treeView1.TopNode == null)
            {
                treeView1.Nodes.Add(node);
            }
            else
            {

                var top = treeView1.TopNode;
                while (top.NextNode != null)
                {
                    top = top.NextNode;
                }
                top.Nodes.Add(node);

            }
        }

        private GrayscaleImage OpenImage(Parameters param, GrayscaleImage a)
        {
            var img = Image.FromFile(param.FilePath);
            return new GrayscaleImage(img);

        }

        private async void Button2Click(object sender, EventArgs e)
        {
            //FilterOptions filterOptions = GetFilterOptions();
            //GrayscaleImage imageWithNoise = null;
            ////await Task<GrayscaleImage>.Factory.StartNew(() => AddNoiseToImage(Img, filterOptions))
            ////              .ContinueWith(task => { imageWithNoise = task.Result; }, TaskContinuationOptions.OnlyOnRanToCompletion);

            //// pictureBox1.Image = imageWithNoise.GetBitmap();
            //imageWithNoise.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "noise.bmp"), ImageFormat.Bmp);

            //GaussFilterOptions gaussFilterOptions = GetGaussFilterOptions();

            //// ReSharper disable CSharpWarnings::CS4014
            //Task<GrayscaleImage>.Factory.StartNew(
            //    () => imageWithNoise.GaussianFilter(gaussFilterOptions.Sigma, gaussFilterOptions.IsKernelSpecified ? gaussFilterOptions.Kernel : 0)
            // ,
            //    TaskCreationOptions.LongRunning)
            //                    .ContinueWith(task =>
            //                        // ReSharper restore CSharpWarnings::CS4014
            //                                  task.Result.GetBitmap()
            //                                      .Save(
            //                                          Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "gauss.bmp"),
            //                                          ImageFormat.Bmp), TaskContinuationOptions.OnlyOnRanToCompletion);
            var gaParam = GetGaussFilterOptions();
            var node = new TreeNode(String.Format("Rozmycie Gaussa {0}, {1}", gaParam.Sigma, gaParam.Kernel));

            var parameters = new Parameters { GaussFilterOptions = gaParam };
            var zadanie = new Zadanie(RunGauss, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }

        private GrayscaleImage RunGauss(Parameters param, GrayscaleImage img)
        {
            return img.GaussianFilter(param.GaussFilterOptions.Sigma, param.GaussFilterOptions.IsKernelSpecified ? param.GaussFilterOptions.Kernel : 0);
        }

        private GrayscaleImage RunBilateral(Parameters param, GrayscaleImage img)
        {
            return img.BilateralFilter(param.BiFilterOptions.SigmaS, param.BiFilterOptions.SigmaR,
                param.BiFilterOptions.IsKernelSpecified ? param.BiFilterOptions.Kernel : 0);
        }

        private GaussFilterOptions GetGaussFilterOptions()
        {
            var result = new GaussFilterOptions();
            result.Sigma = double.Parse(textBoxFilterGaussSigma.Text.Replace(',', '.'), CultureInfo.InvariantCulture);

            int kernelValue;
            if (int.TryParse(textBoxFilterGausKernel.Text, out kernelValue) && (kernelValue > 0))
            {
                result.Kernel = kernelValue;
                result.IsKernelSpecified = true;
            }


            return result;
        }

        private GrayscaleImage AddNoiseToImage(Parameters param, GrayscaleImage img)
        {
            GrayscaleImage resultImage = img;
            var filterOptions = param.FilterOptions;

            if (filterOptions.IsGauss)
            {
                resultImage = img.GaussianNoise(filterOptions.Sigma);
            }
            else if (filterOptions.IsShot)
            {
                resultImage = img.ShotNoise(filterOptions.Lambda);
            }
            else if (filterOptions.IsSP)
            {
                resultImage = img.SaltAndPepperNoise(filterOptions.PercentP, filterOptions.PercentWhite);
            }

            return resultImage;
        }

        private FilterOptions GetFilterOptions()
        {
            var result = new FilterOptions
                             {
                                 IsGauss = checkBoxGauss.Checked,
                                 IsShot = checkBoxPoisson.Checked,
                                 IsSP = checkBoxSP.Checked,
                                 Sigma = double.Parse(textBoxNoiseSigmaGauss.Text.Replace(',', '.'), CultureInfo.InvariantCulture),
                                 Lambda = double.Parse(textBoxNoiseShotPoiss.Text.Replace(',', '.'), CultureInfo.InvariantCulture),
                                 PercentP = double.Parse(textBoxNoiseSPPixel.Text.Replace(',', '.'), CultureInfo.InvariantCulture),
                                 PercentWhite = double.Parse(textBoxNoiseSPBW.Text.Replace(',', '.'), CultureInfo.InvariantCulture)
                             };

            return result;
        }

        private async void button3_Click(object sender, EventArgs e)
        {
            //FilterOptions filterOptions = GetFilterOptions();
            //GrayscaleImage imageWithNoise = null;
            ////await Task<GrayscaleImage>.Factory.StartNew(() => AddNoiseToImage(Img, filterOptions))
            ////                          .ContinueWith(task => { imageWithNoise = task.Result; }, TaskContinuationOptions.OnlyOnRanToCompletion);

            ////  pictureBox1.Image = imageWithNoise.GetBitmap();
            //imageWithNoise.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "noise.bmp"), ImageFormat.Bmp);

            //BiFilterOptions biFilterOptions = GetBiFilterOptions();

            //// ReSharper disable CSharpWarnings::CS4014
            //if (TokenB.Token.CanBeCanceled)
            //{
            //    TokenB.Cancel();
            //    TokenB.Token.WaitHandle.WaitOne();
            //}

            //TokenB = new CancellationTokenSource();

            //Task<GrayscaleImage>.Factory.StartNew(
            //   () =>
            //   imageWithNoise.BilateralFilter(biFilterOptions.SigmaS, biFilterOptions.SigmaR,
            //                                  biFilterOptions.IsKernelSpecified ? biFilterOptions.Kernel : 0)
            //                         , TokenB.Token)
            //                   .ContinueWith(task =>
            //                                     // ReSharper restore CSharpWarnings::CS4014
            //                                     {
            //                                         //  pictureBox3.Image = task.Result.GetBitmap();
            //                                         // ReSharper disable ConvertToLambdaExpression
            //                                         task.Result.GetBitmap()
            //                                             .Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "bi.bmp"),
            //                                                   ImageFormat.Bmp);
            //                                         // ReSharper restore ConvertToLambdaExpression
            //                                     });
            var biFilterOptions = GetBiFilterOptions();
            var node = new TreeNode(String.Format("Bilateralny R:{0},S:{1},{2}", biFilterOptions.SigmaR, biFilterOptions.SigmaS, biFilterOptions.Kernel));

            var parameters = new Parameters { BiFilterOptions = biFilterOptions };
            var zadanie = new Zadanie(RunBilateral, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }

        private BiFilterOptions GetBiFilterOptions()
        {

            var result = new BiFilterOptions();
            result.SigmaS = double.Parse(textBoxfilterBiSigmaSpace.Text.Replace(',', '.'), CultureInfo.InvariantCulture);
            result.SigmaR = double.Parse(textBoxFilterBiSigmaRange.Text.Replace(',', '.'), CultureInfo.InvariantCulture);
            int kernelValue;
            if (int.TryParse(textBoxFilterBiKernel.Text, out kernelValue) && (kernelValue > 0))
            {
                result.Kernel = kernelValue;
                result.IsKernelSpecified = true;
            }


            return result;

        }



        private void treeView1_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void treeView1_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
            {
                Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
                TreeNode destinationNode = ((TreeView)sender).GetNodeAt(pt);
                var newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
                if (destinationNode != null && destinationNode.TreeView == newNode.TreeView)
                {
                    var a = (TreeNode)newNode.Clone();
                    var oldHash = newNode.GetHashCode();
                    var newHash = a.GetHashCode();
                    Zadania.Add(newHash, Zadania[oldHash]);
                    Zadania.Remove(oldHash);
                    destinationNode.Nodes.Add(a);
                    destinationNode.Expand();
                    newNode.Remove();
                }
            }
        }




        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            DoDragDrop(e.Item, DragDropEffects.Move);
        }

        private void treeView1_KeyUp(object sender, KeyEventArgs e)
        {
            var tree = (TreeView)sender;
            if (tree.SelectedNode == null) return;
            if (e.KeyCode == Keys.Delete)
            {
                tree.SelectedNode.Remove();
            }
            if (e.KeyCode == Keys.Q)
            {
                tree.SelectedNode.MoveUp();
            }
            if (e.KeyCode == Keys.A)
            {
                tree.SelectedNode.MoveDown();
            }


        }

        private async void Button8Click(object sender, EventArgs e)
        {
            SNRS.Clear();
            PerformAllOperations(treeView1);
        }

        private void PerformAllOperations(TreeView treeView)
        {
            var node = treeView.TopNode;
            if (node != null)
            {
                PerformNodeOperation(node, null);
            }

        }
        private void PerformNodeOperation(TreeNode node, GrayscaleImage image)
        {
            var zadanie = Zadania[node.GetHashCode()];
            var img = zadanie.Run(image);
            var child = node.FirstNode;
            if (child != null)
            {
                PerformNodeOperation(child, img);
            }
            var sibing = node.NextNode;
            if (sibing != null)
            {
                PerformNodeOperation(sibing, img);
            }
        }
        /// <summary>
        /// Szum gausa
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            var noiseOptions = GetFilterOptions();
            noiseOptions.IsGauss = true;
            var node = new TreeNode(String.Format("Szum Gaussa {0}", noiseOptions.Sigma));
            var parameters = new Parameters { FilterOptions = noiseOptions };
            var zadanie = new Zadanie(AddNoiseToImage, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }
        /// <summary>
        /// Szum poisona
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click(object sender, EventArgs e)
        {
            var noiseOptions = GetFilterOptions();
            noiseOptions.IsShot = true;
            var node = new TreeNode(String.Format("Szum Poissona {0}", noiseOptions.Lambda));
            var parameters = new Parameters { FilterOptions = noiseOptions };
            var zadanie = new Zadanie(AddNoiseToImage, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }
        /// <summary>
        /// S & P 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            var noiseOptions = GetFilterOptions();
            noiseOptions.IsSP = true;
            var node = new TreeNode(String.Format("Szum S&P {0},{1}", noiseOptions.PercentP, noiseOptions.PercentWhite));
            var parameters = new Parameters { FilterOptions = noiseOptions };
            var zadanie = new Zadanie(AddNoiseToImage, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }
        /// <summary>
        /// Zapisz plik
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button7_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();

            saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            saveFileDialog1.FilterIndex = 2;
            saveFileDialog1.RestoreDirectory = true;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                var node = new TreeNode(String.Format("Zapisz Plik {0}", Path.GetFileName(saveFileDialog1.FileName)));
                AddNodeToTree(node);
                var parameters = new Parameters { FilePath = saveFileDialog1.FileName };
                var zadanie = new Zadanie(SaveImage, parameters);
                Zadania.Add(node.GetHashCode(), zadanie);
            }
        }

        private GrayscaleImage SaveImage(Parameters param, GrayscaleImage image)
        {
            var ext = Path.GetExtension(param.FilePath);
            ImageFormat format = ImageFormat.Bmp;
            if (ext != null && ext.ToLower() == "jpg")
            {
                format = ImageFormat.Jpeg;
            }
            image.GetBitmap().Save(param.FilePath, format);
            return image;
        }

        public Dictionary<int, GrayscaleImage> SnrImages { get; set; }
        public int NumerOfSnrSeeds { get; set; }
        private void button9_Click(object sender, EventArgs e)
        {
            NumerOfSnrSeeds++;
            var snrKey = NumerOfSnrSeeds;
            var node = new TreeNode(String.Format("SNR poczatkowy {0}", snrKey));
            var parameters = new Parameters { SnrKey = snrKey };
            var zadanie = new Zadanie(GetSnrSeed, parameters);
            comboBox1.Items.Add(snrKey);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }

        private GrayscaleImage GetSnrSeed(Parameters param, GrayscaleImage image)
        {
            if (SnrImages == null)
            {
                SnrImages = new Dictionary<int, GrayscaleImage>();
            }
            if (SnrImages.ContainsKey(param.SnrKey))
            {
                SnrImages.Remove(param.SnrKey);
            }
            SnrImages.Add(param.SnrKey, image);
            return image;
        }

        public int SnrClients { get; set; }
        private void button10_Click(object sender, EventArgs e)
        {
            if (comboBox1.Text == "")
            {
                return;
            }
            var snrKey = int.Parse(comboBox1.Text);
            SnrClients++;
            var node = new TreeNode(String.Format("SNR  {0}-{1}", snrKey, SnrClients));
            var parameters = new Parameters { SnrKey = snrKey, SnrClientKey = SnrClients };
            var zadanie = new Zadanie(CalculateSnr, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }

        private GrayscaleImage CalculateSnr(Parameters param, GrayscaleImage image)
        {
            var snr = ImageQuality.CalculateSNR(SnrImages[param.SnrKey], image);
            Debug.WriteLine("Snr of image {0} is {1}", param.SnrClientKey, snr);
            SNRS.Add(new SNR(snr, param.SnrClientKey, param.SnrKey));
            return image;
        }

        private void Button11Click(object sender, EventArgs e)
        {
            var node = new TreeNode(String.Format("Pusty"));
            var parameters = new Parameters();
            var zadanie = new Zadanie(Empty, parameters);
            Zadania.Add(node.GetHashCode(), zadanie);
            AddNodeToTree(node);
        }

        private GrayscaleImage Empty(Parameters arg1, GrayscaleImage arg2)
        {
            return arg2;
        }

        private void button12_Click(object sender, EventArgs e)
        {
            var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "snr.txt");
            var group = SNRS.OrderBy(snr => snr.Seed).ThenBy(snr => snr.Result);
            using (var stream = File.CreateText(path))
            {
                var groupN = "";
                foreach (SNR snr in group)
                {
                    if (groupN != snr.Seed.ToString())
                    {
                        stream.WriteLine("Seed: {0}", snr.Seed);
                        groupN = snr.Seed.ToString();
                    }
                    stream.WriteLine(snr.Value);
                }
            }

        }

        private void button13_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10; i++)
            {
                Button2Click(sender, e);
                button10_Click(sender, e);
            }

        }

        private void button14_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10; i++)
            {
                button3_Click(sender, e);
                button10_Click(sender, e);
            }

        }

        private void button15_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 2;
            openFileDialog.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                SNRS.Clear();
                var image = Image.FromFile(openFileDialog.FileName);
                var img = new GrayscaleImage(image);
                var gSigma = 3;
                var bSigma1 = 3;
                var bsigma2 = 0.1;
                var kernel = 3;
                //+ 0.1
                var tmpa = img.GaussianNoise(30);
                tmpa.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "szumG4.jpg"), ImageFormat.Jpeg);
                Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 1, "t01bi");
                G(tmpa.Clone(), gSigma, kernel, img, 2, "tgi");


              
                bsigma2 = 0.25;
                Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 3, "t25bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 4, "gi");

                //var tmpa = img.GaussianNoise(2);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img,1, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 2, "pigi");

                //tmpa = img.GaussianNoise(4);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 3, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 4, "pigi");

                //tmpa = img.GaussianNoise(8);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 5, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 6, "pigi");

                //tmpa = img.GaussianNoise(16);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 7, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 8, "pigi");

                //tmpa = img.GaussianNoise(20);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 9, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 10, "pigi");
                //tmpa = img.GaussianNoise(30);

                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 11, "pi01bi");
                //G(tmpa.Clone(), gSigma, kernel, img, 12, "pigi");

                ////+ 0.3
                //tmpa = img.SaltAndPepperNoise(0.3);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 5, "b3");
                //G(tmpa.Clone(), gSigma, kernel, img, 6, "g3");

                ////+ 0.4
                //tmpa = img.SaltAndPepperNoise(0.4);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 7, "b4");
                //G(tmpa.Clone(), gSigma, kernel, img, 8, "g4");

                ////+ 0.5
                //tmpa = img.SaltAndPepperNoise(0.5);
                //Bi(tmpa.Clone(), bSigma1, bsigma2, kernel, img, 9, "b5");
                //G(tmpa.Clone(), gSigma, kernel, img, 10, "g5");
            }

        }

        private void G(GrayscaleImage tmpa, int gSigma, int kernel, GrayscaleImage img, int rodzina, string nazwa)
        {
            var tmp = tmpa.Clone();
            var snr = ImageQuality.CalculateSNR(img, tmp);
            SNRS.Add(new SNR(snr, -1, rodzina));
            for (int i = 0; i < 15; i++)
            {
                if (i == 1 || i == 5 || i == 10)
                {
                    tmp.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), nazwa + i + ".jpg"), ImageFormat.Jpeg);
                }
                tmp = tmp.GaussianFilter(gSigma, kernel);
                snr = ImageQuality.CalculateSNR(img, tmp);
                SNRS.Add(new SNR(snr, i, rodzina));
            }
          //  tmp.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), nazwa + ".jpg"), ImageFormat.Jpeg);
        }

        private void Bi(GrayscaleImage tmpa, int bSigma1, double bsigma2, int kernel, GrayscaleImage img, int rodzina, string nazwa)
        {
            var tmp = tmpa.Clone();
            var snr = ImageQuality.CalculateSNR(img, tmp);
            SNRS.Add(new SNR(snr, -1, rodzina));
            for (int i = 0; i < 15; i++)
            {
                if (i == 1 || i == 5 || i == 10)
                {
                    tmp.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), nazwa + i + ".jpg"), ImageFormat.Jpeg);
                }
                tmp = tmp.BilateralFilter(bSigma1, bsigma2, kernel);
                snr = ImageQuality.CalculateSNR(img, tmp);
                SNRS.Add(new SNR(snr, i, rodzina));
            }
         //   tmp.GetBitmap().Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), nazwa + ".jpg"), ImageFormat.Jpeg);
        }
    }

    public class SNR
    {
        public SNR(double value, int result, int seed)
        {
            Value = value;
            Result = result;
            Seed = seed;
        }

        public int Seed { get; set; }
        public int Result { get; set; }
        public double Value { get; set; }
    }

    public class Zadanie
    {
        private readonly Func<Parameters, GrayscaleImage, GrayscaleImage> _func;
        private readonly Parameters _param;

        public Zadanie(Func<Parameters, GrayscaleImage, GrayscaleImage> func, Parameters parameters)
        {
            _func = func;
            _param = parameters;
        }

        public GrayscaleImage Run(GrayscaleImage img)
        {
            return _func(_param, img);
        }
    }

    public class Parameters
    {
        public BiFilterOptions BiFilterOptions { get; set; }
        public GaussFilterOptions GaussFilterOptions { get; set; }
        public FilterOptions FilterOptions { get; set; }
        public string FilePath { get; set; }
        public int SnrKey { get; set; }
        public int SnrClientKey { get; set; }
    }

    public class BiFilterOptions
    {
        public double SigmaS { get; set; }
        public double SigmaR { get; set; }
        public int Kernel { get; set; }
        public bool IsKernelSpecified { get; set; }
    }

    public class GaussFilterOptions
    {
        public double Sigma { get; set; }
        public int Kernel { get; set; }
        public bool IsKernelSpecified { get; set; }
    }

    public class FilterOptions
    {
        public bool IsGauss { get; set; }
        public bool IsShot { get; set; }
        public bool IsSP { get; set; }

        public double Sigma { get; set; }
        public double Lambda { get; set; }
        public double PercentP { get; set; }
        public double PercentWhite { get; set; }
    }
    public static class Extensions
    {
        public static void MoveUp(this TreeNode node)
        {
            TreeNode parent = node.Parent;
            TreeView view = node.TreeView;
            if (parent != null)
            {
                int index = parent.Nodes.IndexOf(node);
                if (index > 0)
                {
                    parent.Nodes.RemoveAt(index);
                    parent.Nodes.Insert(index - 1, node);
                }
            }
            else if (node.TreeView.Nodes.Contains(node)) //root node
            {
                int index = view.Nodes.IndexOf(node);
                if (index > 0)
                {
                    view.Nodes.RemoveAt(index);
                    view.Nodes.Insert(index - 1, node);
                }
            }
        }

        public static void MoveDown(this TreeNode node)
        {
            TreeNode parent = node.Parent;
            TreeView view = node.TreeView;
            if (parent != null)
            {
                int index = parent.Nodes.IndexOf(node);
                if (index < parent.Nodes.Count - 1)
                {
                    parent.Nodes.RemoveAt(index);
                    parent.Nodes.Insert(index + 1, node);
                }
            }
            else if (view != null && view.Nodes.Contains(node)) //root node
            {
                int index = view.Nodes.IndexOf(node);
                if (index < view.Nodes.Count - 1)
                {
                    view.Nodes.RemoveAt(index);
                    view.Nodes.Insert(index + 1, node);
                }
            }
        }
    }
}
